package com.app.common.core;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.binding.MapperProxy;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.app.common.util.SpringContext;

public interface IBaseDAO<T, PK extends Serializable> {
	int BATCH_SIZE = 500;

	T queryById(PK id);

	List<T> queryAll(@Param("params") Map<String, Object> params);

	Long queryCount(@Param("params") Map<String, Object> params);

	Integer save(T entity);

	Integer update(T entity);

	int deleteByIds(@Param("ids") List<PK> ids);

	/**
	 * 
	 * Description: 提供批量插入和修改功能 <br/>
	 * 使用样例:<br/>
	 * dao.executeBatch(list, (dao, o) -> dao.saveXXX(o), 1000); <br/>
	 * Date:2021年4月6日 上午8:47:19
	 * 
	 * @author xuejianxin
	 * @param entities   数据
	 * @param callMethod 调用方法
	 * @return
	 */
	@SuppressWarnings("unchecked")
	default <DAO extends IBaseDAO<T, PK>> Integer executeBatch(List<T> entities, BiConsumer<DAO, T> callMethod,
			int batchSize) {
		Class<T> daoClass = getMapperInterface(this);
		// System.out.println(daoClass.toString());
		SqlSessionFactory sqlSessionFactory = SpringContext.get().getBean(SqlSessionFactory.class);
		try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
			DAO dao = (DAO) sqlSession.getMapper(daoClass);
			batchSize = Math.max(batchSize, BATCH_SIZE);
			for (int index = 0; index < entities.size(); index++) {
				T obj = entities.get(index);
				callMethod.accept(dao, obj);// 执行mapper操作
				if (index > 0 && index % batchSize == 0) {
					sqlSession.commit();
				}
			}
			// System.out.println(1/0);//测试事物回滚
			sqlSession.commit();
		}
		return entities.size();
	}

	/**
	 * 
	 * Description: 批量执行insert <br/>
	 * dao.saveBatch(list); <br/>
	 * Date:2021年4月6日 下午3:27:02
	 * 
	 * @author xuejianxin
	 * @param entities
	 * @return
	 */
	default Integer saveBatch(List<T> entities) {
		return executeBatch(entities, (dao, en) -> dao.save(en), BATCH_SIZE);
	}

	/**
	 * 
	 * Description: 批量执行update <br/>
	 * dao.updateBatch(list); <br/>
	 * Date:2021年4月6日 下午3:27:34
	 * 
	 * @author xuejianxin
	 * @param <DAO>
	 * @param entities
	 * @return
	 */
	default Integer updateBatch(List<T> entities) {
		return executeBatch(entities, (dao, en) -> dao.update(en), BATCH_SIZE);
	}

	@SuppressWarnings("unchecked")
	private Class<T> getMapperInterface(Object pvMapper) {
		try {
			if (!Proxy.isProxyClass(pvMapper.getClass()))
				return null;
			InvocationHandler lvInvHandler = Proxy.getInvocationHandler(pvMapper);
			if (!(lvInvHandler instanceof MapperProxy))
				return null;
			MapperProxy<?> lvT = (MapperProxy<?>) lvInvHandler;
			Field lvSessField = lvT.getClass().getDeclaredField("mapperInterface");
			lvSessField.setAccessible(true);
			return (Class<T>) lvSessField.get(lvT);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// https://blog.csdn.net/rocklee/article/details/88762361
	@Deprecated
	private SqlSession getSqlSessionFromMapperInstance(Object pvMapper) {
		if (!Proxy.isProxyClass(pvMapper.getClass()))
			return null;
		InvocationHandler lvInvHandler = Proxy.getInvocationHandler(pvMapper);
		if (!(lvInvHandler instanceof MapperProxy))
			return null;
		MapperProxy<?> lvT = (MapperProxy<?>) lvInvHandler;
		try {
			Field lvSessField = lvT.getClass().getDeclaredField("sqlSession");
			lvSessField.setAccessible(true);
			try {
				return (SqlSession) lvSessField.get(lvT);
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		} catch (NoSuchFieldException | SecurityException e) {
			e.printStackTrace();
		}
		return null;
	}

}